﻿/********************************************************************
	created:	2011-8-17   8:26
	author:		Lifan Su
	
	purpose:	Implement of SHA-256 functions.
*********************************************************************/

#include "sha256.h"
#include <cvt_int_mem.h>
#include "hex_to_char.h"

#include <assert.h>
#include <_stdint.h>
#include <string.h>


static const int blocksize = 0x40;
static const int blockmask = 0x3F;
static const int endmaxsize = 0x37;
static const uint8_t paddingch1 = 0x80;
static const uint8_t paddingcha = 0x00;

static const uint32_t sha256_const[64] = {
    UINT32_C(0x428a2f98), UINT32_C(0x71374491), UINT32_C(0xb5c0fbcf), UINT32_C(0xe9b5dba5),
    UINT32_C(0x3956c25b), UINT32_C(0x59f111f1), UINT32_C(0x923f82a4), UINT32_C(0xab1c5ed5),
    UINT32_C(0xd807aa98), UINT32_C(0x12835b01), UINT32_C(0x243185be), UINT32_C(0x550c7dc3),
    UINT32_C(0x72be5d74), UINT32_C(0x80deb1fe), UINT32_C(0x9bdc06a7), UINT32_C(0xc19bf174),

    UINT32_C(0xe49b69c1), UINT32_C(0xefbe4786), UINT32_C(0x0fc19dc6), UINT32_C(0x240ca1cc),
    UINT32_C(0x2de92c6f), UINT32_C(0x4a7484aa), UINT32_C(0x5cb0a9dc), UINT32_C(0x76f988da),
    UINT32_C(0x983e5152), UINT32_C(0xa831c66d), UINT32_C(0xb00327c8), UINT32_C(0xbf597fc7),
    UINT32_C(0xc6e00bf3), UINT32_C(0xd5a79147), UINT32_C(0x06ca6351), UINT32_C(0x14292967),

    UINT32_C(0x27b70a85), UINT32_C(0x2e1b2138), UINT32_C(0x4d2c6dfc), UINT32_C(0x53380d13),
    UINT32_C(0x650a7354), UINT32_C(0x766a0abb), UINT32_C(0x81c2c92e), UINT32_C(0x92722c85),
    UINT32_C(0xa2bfe8a1), UINT32_C(0xa81a664b), UINT32_C(0xc24b8b70), UINT32_C(0xc76c51a3),
    UINT32_C(0xd192e819), UINT32_C(0xd6990624), UINT32_C(0xf40e3585), UINT32_C(0x106aa070),

    UINT32_C(0x19a4c116), UINT32_C(0x1e376c08), UINT32_C(0x2748774c), UINT32_C(0x34b0bcb5),
    UINT32_C(0x391c0cb3), UINT32_C(0x4ed8aa4a), UINT32_C(0x5b9cca4f), UINT32_C(0x682e6ff3),
    UINT32_C(0x748f82ee), UINT32_C(0x78a5636f), UINT32_C(0x84c87814), UINT32_C(0x8cc70208),
    UINT32_C(0x90befffa), UINT32_C(0xa4506ceb), UINT32_C(0xbef9a3f7), UINT32_C(0xc67178f2)
};

static uint32_t rotl(uint32_t val, uint32_t n);
static void block_calc(struct SHA256_c *val, const void *buf);


CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_d_read(struct SHA256_d *dst, const void *src)
{
    const uint8_t *_src = src;

    assert(dst && src && "Non-null pointer");

    dst->hash[0] = cvt_byte_to_u32_be(_src +  0);
    dst->hash[1] = cvt_byte_to_u32_be(_src +  4);
    dst->hash[2] = cvt_byte_to_u32_be(_src +  8);
    dst->hash[3] = cvt_byte_to_u32_be(_src + 12);
    dst->hash[4] = cvt_byte_to_u32_be(_src + 16);
    dst->hash[5] = cvt_byte_to_u32_be(_src + 20);
    dst->hash[6] = cvt_byte_to_u32_be(_src + 24);
    dst->hash[7] = cvt_byte_to_u32_be(_src + 28);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_d_write(void *dst, const struct SHA256_d *src)
{
    uint8_t *_dst = dst;

    assert(dst && src && "Non-null pointer");

    cvt_u32_be_to_byte(_dst +  0, src->hash[0]);
    cvt_u32_be_to_byte(_dst +  4, src->hash[1]);
    cvt_u32_be_to_byte(_dst +  8, src->hash[2]);
    cvt_u32_be_to_byte(_dst + 12, src->hash[3]);
    cvt_u32_be_to_byte(_dst + 16, src->hash[4]);
    cvt_u32_be_to_byte(_dst + 20, src->hash[5]);
    cvt_u32_be_to_byte(_dst + 24, src->hash[6]);
    cvt_u32_be_to_byte(_dst + 28, src->hash[7]);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_d_print(char *dst, const struct SHA256_d *src)
{
    int i, j;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 8; ++i) {
        for (j = 8; j != 0; --j) {
            *(dst++) = hex_to_char(((src->hash[i]) >> (4 * j - 4)) & 0xF);
        }
    }

    *dst = '\0';
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_d_printw(wchar_t *dst, const struct SHA256_d *src)
{
    int i, j;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 8; ++i) {
        for (j = 8; j != 0; --j) {
            *(dst++) = hex_to_wchar(((src->hash[i]) >> (4 * j - 4)) & 0xF);
        }
    }

    *dst = L'\0';
}

CRYPT_HASH_PREFIX uint8_t CRYPT_HASH_API sha256_d_less(const struct SHA256_d *lhs, const struct SHA256_d *rhs)
{
    assert(lhs && rhs && "Non-null pointer");

    return (lhs->hash[0] < rhs->hash[0]) || ((lhs->hash[0] == rhs->hash[0]) 
        && ((lhs->hash[1] < lhs->hash[1]) || ((lhs->hash[1] == rhs->hash[1])
        && ((lhs->hash[2] < rhs->hash[2]) || ((lhs->hash[2] == rhs->hash[2])
        && ((lhs->hash[3] < rhs->hash[3]) || ((lhs->hash[3] == rhs->hash[3])
        && ((lhs->hash[4] < rhs->hash[4]) || ((lhs->hash[4] == rhs->hash[4])
        && ((lhs->hash[5] < rhs->hash[5]) || ((lhs->hash[5] == rhs->hash[5])
        && ((lhs->hash[6] < rhs->hash[6]) || ((lhs->hash[6] == rhs->hash[6])
        && ((lhs->hash[7] < rhs->hash[7])))))))))))))));
}

CRYPT_HASH_PREFIX uint8_t CRYPT_HASH_API sha256_d_eq(const struct SHA256_d *lhs, const struct SHA256_d *rhs)
{
    assert(lhs && rhs && "Non-null pointer");

    return (lhs->hash[0] == rhs->hash[0]) && (lhs->hash[1] == rhs->hash[1])
        && (lhs->hash[2] == rhs->hash[2]) && (lhs->hash[3] == rhs->hash[3])
        && (lhs->hash[4] == rhs->hash[4]) && (lhs->hash[5] == rhs->hash[5])
        && (lhs->hash[6] == rhs->hash[6]) && (lhs->hash[7] == rhs->hash[7]);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_cvt(struct SHA256_d *dst, const struct SHA256_c *src)
{
    int i;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 8; ++i) {
        dst->hash[i] = src->hash[i];
    }
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_c_init(struct SHA256_c *val)
{
    assert(val && "Non-null pointer");

    val->hash[0] = UINT32_C(0x6a09e667);
    val->hash[1] = UINT32_C(0xbb67ae85);
    val->hash[2] = UINT32_C(0x3c6ef372);
    val->hash[3] = UINT32_C(0xa54ff53a);
    val->hash[4] = UINT32_C(0x510e527f);
    val->hash[5] = UINT32_C(0x9b05688c);
    val->hash[6] = UINT32_C(0x1f83d9ab);
    val->hash[7] = UINT32_C(0x5be0cd19);

    val->length = 0;
    val->append = 1;
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_c_append(struct SHA256_c *val, const void *src, uint64_t len)
{
    int buffer_used;
    const uint8_t *_src = src;

    assert(val && src && val->append && "Non-null pointer and append able");

    buffer_used = val->length & blockmask;
    val->length += len;
    while (len >= blocksize - buffer_used) {
        if (buffer_used) {
            memcpy(val->buffer + buffer_used, _src, blocksize - buffer_used);
            block_calc(val, val->buffer);
            _src += (blocksize - buffer_used);
            len -= (blocksize - buffer_used);
            buffer_used = 0;
        } else {
            block_calc(val, _src);
            _src += blocksize;
            len -= blocksize;
        }
    }

    memcpy(val->buffer + buffer_used, _src, (size_t)len);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha256_c_close(struct SHA256_c *val)
{
    int i = 0;

    assert(val && val->append && "Non-null pointer and append able");

    if ((val->length & blockmask) <= endmaxsize) {
        val->buffer[val->length & blockmask] = paddingch1;
        memset(val->buffer + (val->length & blockmask) + 1, paddingcha,
            endmaxsize - (val->length & blockmask));
        cvt_u64_be_to_byte(val->buffer + endmaxsize + 1, val->length << 3);
        block_calc(val, val->buffer);
    } else {
        val->buffer[val->length & blockmask] = paddingch1;
        memset(val->buffer + (val->length & blockmask) + 1, paddingcha,
            blocksize - 1 - (val->length & blockmask));
        block_calc(val, val->buffer);
        memset(val->buffer,  paddingcha, blocksize - 8);
        cvt_u64_be_to_byte(val->buffer + endmaxsize + 1, val->length << 3);
        block_calc(val, val->buffer);
    }

    val->append = 0;
}


static uint32_t rotr(uint32_t val, uint32_t n)
{
    return (val >> n) | (val << (32-n));
}

static void block_calc(struct SHA256_c *val, const void *buf)
{
    int i;
    uint32_t t1, t2;
    const uint8_t *_buf;

    uint32_t words_arr[64];
    uint32_t buf_arr[8];

    _buf = buf;

    for (i = 0; i != 8; ++i) {
        buf_arr[i] = val->hash[i];
    }
    for (i = 0; i != 16; ++i) {
        words_arr[i] = cvt_byte_to_u32_be(_buf + 4 * i);
    }
    for (i = 16; i != 64; ++i) {
        words_arr[i] = words_arr[i - 16] + words_arr[i - 7]
        + (rotr(words_arr[i - 15], 7)
            ^ rotr(words_arr[i - 15], 18)
            ^ words_arr[i - 15] >> 3)
            + (rotr(words_arr[i - 2], 17)
            ^ rotr(words_arr[i - 2], 19)
            ^ words_arr[i - 2] >> 10);
    }

    for (i = 0; i != 64; ++i) {
        t1 = buf_arr[7] + sha256_const[i] + words_arr[i]
        + (buf_arr[4] & buf_arr[5] ^ ~buf_arr[4] & buf_arr[6])
            + (rotr(buf_arr[4], 6) ^ rotr(buf_arr[4], 11) ^ rotr(buf_arr[4], 25));
        t2 = (rotr(buf_arr[0], 2) ^ rotr(buf_arr[0], 13) ^ rotr(buf_arr[0], 22))
            + (buf_arr[0] & buf_arr[1] ^ buf_arr[1] & buf_arr[2] ^ buf_arr[2] & buf_arr[0]);

        buf_arr[7] = buf_arr[6];
        buf_arr[6] = buf_arr[5];
        buf_arr[5] = buf_arr[4];
        buf_arr[4] = buf_arr[3] + t1;
        buf_arr[3] = buf_arr[2];
        buf_arr[2] = buf_arr[1];
        buf_arr[1] = buf_arr[0];
        buf_arr[0] = t1 + t2;
    }

    for (i = 0; i != 8; ++i) {
        val->hash[i] += buf_arr[i];
    }
}